import java.util.Vector;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Comparator;
import java.util.Random;
import java.util.Set;
import java.util.Iterator;
/*
	TODO:
	* Initiative (may require some new collection interface for multiple 
		combatants of a common type (ie: 3 identical orcs who would resolve 
		together)
*/
/**
*	The Encounter Object is the top-level container for a combat.  It maintains
*	global functionality, such as logging, and is the actor responsible for
*	executing the combat and providing an interface into the results
*/
public class Encounter
{
	private int iTurns = 0;
	private int iRounds = 0;
	private HashMap<Combatant, Integer> hmInitiative;
	private TreeMap<Combatant, Integer> tmInitiative;
	private InitComp cComp;
	private Vector<Party> vParties;
	private Random rGenerator;
	/** Determine whether or not to report blow-by-blow combat*/
	private boolean bVerbose = false;
	public Encounter()
	{
		vParties = new Vector<Party>();
		hmInitiative = new HashMap<Combatant,Integer>();
		cComp = new InitComp(hmInitiative);
		tmInitiative = new TreeMap<Combatant,Integer>(cComp);
		rGenerator  = new Random();
	}
	public void addParty(Party pAddThis)
	{
		pAddThis.setEncounter(this);
		vParties.add(pAddThis);
	}
	public int getTurns()
	{
		return iTurns;
	}
	public int getRounds()
	{
		return iRounds;
	}
	/** 
	*	getORParty returns a random party other than the one provided it
	*/
	public Party getORParty(Party pParty)
	{
		Vector <Party>vAvailable = new Vector<Party>();
		for(Party p: vParties)
		{
			if(p!=pParty && p.isAlive())
			{
				vAvailable.add(p);
			}
		}
		if(vAvailable.size() > 0)
		{
			int i = rGenerator.nextInt(vAvailable.size());
			return vAvailable.elementAt(i);
		}
		else
		{
			return null;
		}
	}
	
	public String getVictor()
	{
		for(Party p:vParties)
		{
			if(p.isAlive())
			{
				return p.getName(); 
			}
		}
		return null;
	}
	/** setVerbose tells the encounter whether or not to report
	* 	blow-by-blow combat
	*/
	public void setVerbose(boolean bMode)
	{
		bVerbose = bMode;
	}
	
	/**
	* 	printLog prints the message to the screen when verbose mode is true
	*/
	public void printLog(String sMessage)
	{
		// eventually logging to a file might be nice as well
		if (bVerbose)
		{
			System.out.println(sMessage);
		}
	}
	/**
	*	stillGoing verifies that more than one party is still combat ready
	*/
	private boolean stillGoing()
	{
		int iFighting = 0;
		for(Party p: vParties)
		{
			if(p.isAlive())
			{
				iFighting++;
			}
		}
		if(iFighting > 1)
		{
			return true;
		}
		return false;
	}
	/**
	*	resolveCombat iterates through the various party's members, having them 
	*	attack until only one party remains standing
	*/
	public void resolveCombat()
	{
		String sMessage;
		rollInitiative();
		while(stillGoing())
		{
			iRounds++;
			sMessage = "\n** Round "+iRounds+" is starting. **";
			printLog(sMessage);
			for(Combatant c: tmInitiative.keySet())
			{
				c.doTurn(this);
				iTurns++;
			}
			/*for(Party p: vParties)
			{
				for(int i = 0; i < p.unitsLeft(); i++)
				{
					Combatant unit =  p.getUnit(i);
					unit.doTurn(this);
					iTurns++;
				}
			}
			*/
		}
	}
	/**
	*	rollInitiative determines the order of resolution for the combatants
	*	contained in the party objects.
	*	Does not currently work because combatant is not comparable- since
	*	we plan on allowing a group of combatants to be designated as resolving
	*	together, this code will be unused pending the implementation of a 
	*	ResolvesWith class
	*	reading (since I am still learning java): 
	*	http://lkamal.blogspot.com/2008/07/java-sorting-comparator-vs-comparable.html
	*	http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java
	*/
	public void rollInitiative()
	{
		for (Party p: vParties)
		{
			for(int i = 0; i < p.unitsLeft(); i++)
			{
				Combatant unit = p.getUnit(i);
				Integer iNum = rGenerator.nextInt(20) +1 + unit.getInit();
				hmInitiative.put(unit, iNum);
			}
		}
		
		tmInitiative.putAll(hmInitiative); // now combatants are sorted
		for(Combatant c: tmInitiative.keySet())
		{
			printLog("Combatant/Initiative: " + c.getName() + "/"+tmInitiative.get(c));
		}
		//stopgap.  surprisingly tricky for the comparator to handle this fairlys
		if(tmInitiative.size() != hmInitiative.size())
		{
			tmInitiative.clear();
			rollInitiative();
		}
		/*
		if(tmInitiative.size() != hmInitiative.size())
		{
			System.out.println("different sizes");
			System.out.println("  TM:");
			for(Combatant c: tmInitiative.keySet())
			{
				System.out.println("Combatant/Initiative: " + c.getName() + "/"+tmInitiative.get(c));
			}
			Set entries = hmInitiative.entrySet();
			Iterator it = entries.iterator();
			System.out.println("  HM:");
			while (it.hasNext()) 
			{
				Map.Entry entry = (Map.Entry) it.next();
				Combatant c1 = (Combatant)entry.getKey();
				System.out.println(c1.getName() + "-->" + entry.getValue() + "/"+c1.getInit());
			}
		}
		*/
	}
}

/**
* InitComp is a comparator for determining which combatant has higher
*	initiative
*/
class InitComp implements Comparator
{
	Map base;
	public InitComp(Map base) 
	{
      this.base = base;
  }

  public int compare(Object a, Object b) {
    if((Integer)base.get(a) < (Integer)base.get(b)) 
    {
      return 1;
    } 
    else if((Integer)base.get(a) == (Integer)base.get(b)) 
    {
    	Combatant c1 = (Combatant) a;
    	Combatant c2 = (Combatant) b;
    	
    	if(c1.getInit() < c2.getInit())
    	{
    		return 1;
    	}
    	else if(c1.getInit() > c2.getInit())
    	{
    		return -1;
    	}
    	else
    	{
    		// if they have the same init and same roll
    		return 0;
    	}
    } 
    else 
    {
      return -1;
    }
  }
}
